import sys
import random
import uuid
import paramiko
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QInputDialog, QMessageBox, QTreeWidget, QTreeWidgetItem, QPushButton, QHBoxLayout, QTextEdit, QSplitter, QFileDialog, QDialog, QLabel, QLineEdit, QFormLayout, QProgressDialog, QListWidget, QComboBox
from PyQt5.QtCore import Qt, QPropertyAnimation, QThread, pyqtSignal
import os
from PyQt5.QtGui import QMovie  # 导入 QMovie
import subprocess
import json

# 默认设置
settings = {
    "REMOTE_HOST": "192.168.3.10",
    "REMOTE_USER": "root",
    "REMOTE_PASS": "nfdw@134.com",
    "LOCAL_HOST": "192.168.1.18",
    "LOG_FILE": "vm_manager.log",
    "ISO_PATH": "/data/iso/",  # ISO 文件存放路径
    "DISK_PATH": "/data/images/",  # 磁盘存放路径
    "XML_TEMPLATE": """<domain type='kvm'>
  <name>{vm_name}</name>
  <uuid>$(uuidgen)</uuid>
  <memory unit='KiB'>{mem_num * 1024}</memory>
  <currentMemory unit='KiB'>{mem_num * 1024}</currentMemory>
  <vcpu placement='static'>{cpu_num}</vcpu>

  <os>
    <type arch='{arch}' machine='virt-6.2'>hvm</type>
    <loader readonly='yes' type='pflash'>/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw</loader>
    <nvram>/var/lib/libvirt/qemu/nvram/{vm_name}_VARS.fd</nvram>
    <boot dev='hd'/>
    <boot dev='cdrom'/>
  </os>

  <features>
    <acpi/>
    <gic version='3'/>
  </features>

  <cpu mode='host-passthrough' check='none'/>
  <clock offset='utc'/>

  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>destroy</on_crash>

  <devices>
    <emulator>/usr/bin/qemu-system-aarch64</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2'/>
      <source file="{settings['DISK_PATH']}/{vm_name}.qcow2"/>
      <target dev='sda' bus='scsi'/>
      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
    </disk>
    <disk type='file' device='cdrom'>
      <driver name='qemu' type='raw'/>
      <source file='{iso_name}'/>
      <target dev='sdb' bus='scsi'/>
      <readonly/>
      <address type='drive' controller='0' bus='0' target='0' unit='1'/>
    </disk>
    <controller type='usb' index='0' model='qemu-xhci' ports='15'>
      <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
    </controller>
    <controller type='scsi' index='0' model='virtio-scsi'>
      <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
    </controller>
    <controller type='pci' index='0' model='pcie-root'/>
    <controller type='virtio-serial' index='0'>
      <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
    </controller>
    <controller type='pci' index='1' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='1' port='0x8'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0' multifunction='on'/>
    </controller>
    <controller type='pci' index='2' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='2' port='0x9'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
    </controller>
    <controller type='pci' index='3' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='3' port='0xa'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
    </controller>
    <controller type='pci' index='4' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='4' port='0xb'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x3'/>
    </controller>
    <controller type='pci' index='5' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='5' port='0xc'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x4'/>
    </controller>
    <controller type='pci' index='6' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='6' port='0xd'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x5'/>
    </controller>
    <interface type='bridge'>
      <source bridge='br0'/>
      <model type='virtio'/>
    </interface>
    <serial type='pty'>
      <target type='system-serial' port='0'>
        <model name='pl011'/>
      </target>
    </serial>
    <console type='pty'>
      <target type='serial' port='0'/>
    </console>
    <channel type='unix'>
      <target type='virtio' name='org.qemu.guest_agent.0'/>
      <address type='virtio-serial' controller='0' bus='0' port='1'/>
    </channel>
    <input type='keyboard' bus='usb'>
      <address type='usb' bus='0' port='1'/>
    </input>
    <input type='mouse' bus='usb'>
      <address type='usb' bus='0' port='2'/>
    </input>
    <input type='tablet' bus='usb'>
      <address type='usb' bus='0' port='3'/>
    </input>
    <graphics type='vnc' port='-1' autoport='yes'>
      <listen type='address'/>
    </graphics>
    <video>
      <model type='virtio' heads='1' primary='yes'/>
      <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
    </video>
  </devices>
</domain>
"""
}

def execute_ssh_command(command):
    try:
        client = paramiko.SSHClient()
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        client.connect(settings["REMOTE_HOST"], username=settings["REMOTE_USER"], password=settings["REMOTE_PASS"])
        stdin, stdout, stderr = client.exec_command(command)
        result = stdout.read().decode('utf-8')
        error = stderr.read().decode('utf-8')
        client.close()
        if error:
            return error
        else:
            return result
    except Exception as e:
        return str(e)

def list_vms():
    # 获取所有虚拟机的状态
    all_vms = execute_ssh_command("virsh list --all --name").splitlines()
    running_vms = execute_ssh_command("virsh list --name").splitlines()
    
    # 创建一个字典来存储虚拟机状态
    vm_status = {vm: "running" if vm in running_vms else "shut off" for vm in all_vms}
    return vm_status

def get_vm_info(vm_name):
    # 获取虚拟机的详细信息
    info = execute_ssh_command(f"virsh dominfo {vm_name}")
    # 获取虚拟机的磁盘信息
    disk_info = execute_ssh_command(f"virsh domblklist {vm_name}")
    # 获取虚拟机的网络接口信息
    network_info = execute_ssh_command(f"virsh domiflist {vm_name}")
    
    # 组合信息
    full_info = f"虚拟机名称: {vm_name}\n\n{info}\n\n磁盘信息:\n{disk_info}\n\n网络接口信息:\n{network_info}"
    return full_info

def create_vm_from_xml():
    # 移除 XML 相关的代码
    QMessageBox.warning(None, "错误", "不再支持通过 XML 创建虚拟机。")

def list_iso_files():
    iso_files = execute_ssh_command("find /home/iso_nfs -name '*.iso'").splitlines()
    return iso_files

class CloneVMThread(QThread):
    finished = pyqtSignal(str)  # 定义一个信号，用于传递结果

    def __init__(self, command):
        super().__init__()
        self.command = command

    def run(self):
        result = execute_ssh_command(self.command)  # 执行克隆命令
        self.finished.emit(result)  # 发射信号，传递结果

class SettingsDialog(QDialog):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("设置")
        self.setGeometry(150, 150, 400, 400)

        layout = QFormLayout()
        self.host_input = QLineEdit(settings["REMOTE_HOST"])
        self.user_input = QLineEdit(settings["REMOTE_USER"])
        self.pass_input = QLineEdit(settings["REMOTE_PASS"])
        self.local_host_input = QLineEdit(settings.get("LOCAL_HOST", ""))  # 新增本机IP地址输入框
        self.log_input = QLineEdit(settings["LOG_FILE"])
        self.disk_path_input = QLineEdit(settings["DISK_PATH"])
        self.xml_template_input = QTextEdit()  # 用于输入 XML 配置
        self.xml_template_input.setPlainText(self.get_default_xml_template())  # 设置默认 XML
        self.iso_path_input = QLineEdit()
        self.iso_path_input.setText(settings["ISO_PATH"])  # 设置默认值

        layout.addRow("远程主机地址:", self.host_input)
        layout.addRow("远程用户名:", self.user_input)
        layout.addRow("远程用户密码:", self.pass_input)
        layout.addRow("本机IP地址:", self.local_host_input)  # 添加本机IP地址输入框
        layout.addRow("日志文件路径:", self.log_input)
        layout.addRow("磁盘存放路径:", self.disk_path_input)
        layout.addRow("ISO 文件存放路径:", self.iso_path_input)
        layout.addRow("默认 XML 配置:", self.xml_template_input)

        save_button = QPushButton("保存")
        save_button.clicked.connect(self.save_settings)
        layout.addWidget(save_button)

        self.setLayout(layout)

    def get_default_xml_template(self):
        return """<domain type='kvm'>
  <name>{vm_name}</name>
  <uuid>$(uuidgen)</uuid>
  <memory unit='KiB'>{mem_num * 1024}</memory>
  <currentMemory unit='KiB'>{mem_num * 1024}</currentMemory>
  <vcpu placement='static'>{cpu_num}</vcpu>

  <os>
    <type arch='{arch}' machine='virt-6.2'>hvm</type>
    <loader readonly='yes' type='pflash'>/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw</loader>
    <nvram>/var/lib/libvirt/qemu/nvram/{vm_name}_VARS.fd</nvram>
    <boot dev='hd'/>
    <boot dev='cdrom'/>
  </os>

  <features>
    <acpi/>
    <gic version='3'/>
  </features>

  <cpu mode='host-passthrough' check='none'/>
  <clock offset='utc'/>

  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>destroy</on_crash>

  <devices>
    <emulator>/usr/bin/qemu-system-aarch64</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2'/>
      <source file="{settings['DISK_PATH']}/{vm_name}.qcow2"/>
      <target dev='sda' bus='scsi'/>
      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
    </disk>
    <disk type='file' device='cdrom'>
      <driver name='qemu' type='raw'/>
      <source file='{iso_name}'/>
      <target dev='sdb' bus='scsi'/>
      <readonly/>
      <address type='drive' controller='0' bus='0' target='0' unit='1'/>
    </disk>
    <controller type='usb' index='0' model='qemu-xhci' ports='15'>
      <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
    </controller>
    <controller type='scsi' index='0' model='virtio-scsi'>
      <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
    </controller>
    <controller type='pci' index='0' model='pcie-root'/>
    <controller type='virtio-serial' index='0'>
      <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
    </controller>
    <controller type='pci' index='1' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='1' port='0x8'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0' multifunction='on'/>
    </controller>
    <controller type='pci' index='2' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='2' port='0x9'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
    </controller>
    <controller type='pci' index='3' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='3' port='0xa'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
    </controller>
    <controller type='pci' index='4' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='4' port='0xb'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x3'/>
    </controller>
    <controller type='pci' index='5' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='5' port='0xc'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x4'/>
    </controller>
    <controller type='pci' index='6' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='6' port='0xd'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x5'/>
    </controller>
    <interface type='bridge'>
      <source bridge='br0'/>
      <model type='virtio'/>
    </interface>
    <serial type='pty'>
      <target type='system-serial' port='0'>
        <model name='pl011'/>
      </target>
    </serial>
    <console type='pty'>
      <target type='serial' port='0'/>
    </console>
    <channel type='unix'>
      <target type='virtio' name='org.qemu.guest_agent.0'/>
      <address type='virtio-serial' controller='0' bus='0' port='1'/>
    </channel>
    <input type='keyboard' bus='usb'>
      <address type='usb' bus='0' port='1'/>
    </input>
    <input type='mouse' bus='usb'>
      <address type='usb' bus='0' port='2'/>
    </input>
    <input type='tablet' bus='usb'>
      <address type='usb' bus='0' port='3'/>
    </input>
    <graphics type='vnc' port='-1' autoport='yes'>
      <listen type='address'/>
    </graphics>
    <video>
      <model type='virtio' heads='1' primary='yes'/>
      <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
    </video>
  </devices>
</domain>
"""  # 这里是默认的 XML 模板

    def save_settings(self):
        settings["REMOTE_HOST"] = self.host_input.text()
        settings["REMOTE_USER"] = self.user_input.text()
        settings["REMOTE_PASS"] = self.pass_input.text()
        settings["LOCAL_HOST"] = self.local_host_input.text()  # 保存本机IP地址
        settings["LOG_FILE"] = self.log_input.text()
        settings["DISK_PATH"] = self.disk_path_input.text()
        settings["ISO_PATH"] = self.iso_path_input.text()
        settings["XML_TEMPLATE"] = self.xml_template_input.toPlainText()  # 保存 XML 配置

        # 保存到 JSON 文件
        with open('settings.json', 'w') as f:
            json.dump(settings, f, indent=4)

        self.accept()

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        load_settings()  # 加载设置
        self.setWindowTitle("虚拟机管理器")
        self.setGeometry(100, 100, 800, 600)

        # 设置样式
        self.setStyleSheet("""
            QMainWindow {
                background-color: #f0f0f0;
            }
            QPushButton {
                background-color: #4CAF50;
                color: white;
                border: none;
                padding: 10px;
                border-radius: 5px;
                font-size: 14px;
            }
            QPushButton:hover {
                background-color: #45a049;
            }
            QLineEdit {
                padding: 10px;
                border: 1px solid #ccc;
                border-radius: 5px;
                font-size: 14px;
            }
            QTextEdit {
                padding: 10px;
                border: 1px solid #ccc;
                border-radius: 5px;
                font-size: 14px;
            }
            QTreeWidget {
                font-size: 14px;
            }
            QSplitter {
                background-color: #e0e0e0;
            }
        """)

        main_layout = QVBoxLayout()

        # 添加本机IP地址输入框
        self.local_host_input = QLineEdit(settings.get("LOCAL_HOST", ""))  # 新增本机IP地址输入框

        # 创建按钮布局
        button_layout = QHBoxLayout()

        self.refresh_button = QPushButton("刷新虚拟机列表")
        self.refresh_button.clicked.connect(self.refresh_vm_list)
        button_layout.addWidget(self.refresh_button)

        self.create_vm_button = QPushButton("创建虚拟机")
        self.create_vm_button.clicked.connect(self.create_vm)
        button_layout.addWidget(self.create_vm_button)

        self.clone_vm_button = QPushButton("克隆虚拟机")
        self.clone_vm_button.clicked.connect(self.clone_vm)
        button_layout.addWidget(self.clone_vm_button)

        self.delete_vm_button = QPushButton("删除虚拟机")
        self.delete_vm_button.clicked.connect(self.delete_selected_vm)
        button_layout.addWidget(self.delete_vm_button)

        self.start_vm_button = QPushButton("启动虚拟机")
        self.start_vm_button.clicked.connect(self.start_selected_vm)
        button_layout.addWidget(self.start_vm_button)

        self.stop_vm_button = QPushButton("关闭虚拟机")
        self.stop_vm_button.clicked.connect(self.stop_selected_vm)
        button_layout.addWidget(self.stop_vm_button)

        self.settings_button = QPushButton("设置")
        self.settings_button.clicked.connect(self.open_settings_dialog)
        button_layout.addWidget(self.settings_button)

        # 添加搜索框
        self.search_input = QLineEdit()
        self.search_input.setPlaceholderText("搜索虚拟机...")
        self.search_input.textChanged.connect(self.filter_vms)

        # 添加打开 VNC 界面的按钮
        self.open_vnc_button = QPushButton("打开 VNC 界面")
        self.open_vnc_button.clicked.connect(self.open_vnc_interface)
        button_layout.addWidget(self.open_vnc_button)

        # 添加树形控件和信息显示区域
        splitter = QSplitter(Qt.Horizontal)

        self.running_vm_tree = QTreeWidget()
        self.running_vm_tree.setHeaderLabels(["正在运行的虚拟机"])
        self.running_vm_tree.itemClicked.connect(self.display_vm_info)
        self.running_vm_tree.itemSelectionChanged.connect(self.update_button_states)
        splitter.addWidget(self.running_vm_tree)

        self.stopped_vm_tree = QTreeWidget()
        self.stopped_vm_tree.setHeaderLabels(["已关闭的虚拟机"])
        self.stopped_vm_tree.itemClicked.connect(self.display_vm_info)
        self.stopped_vm_tree.itemSelectionChanged.connect(self.update_button_states)
        splitter.addWidget(self.stopped_vm_tree)

        self.vm_info_text = QTextEdit()
        self.vm_info_text.setReadOnly(True)
        splitter.addWidget(self.vm_info_text)

        splitter.setSizes([200, 200, 600])

        # 将所有布局添加到主布局中
        main_layout.addLayout(button_layout)
        main_layout.addWidget(self.search_input)
        main_layout.addWidget(splitter)

        container = QWidget()
        container.setLayout(main_layout)
        self.setCentralWidget(container)

        self.refresh_vm_list()

    def refresh_vm_list(self):
        self.running_vm_tree.clear()
        self.stopped_vm_tree.clear()
        vm_statuses = list_vms()  # 获取虚拟机状态字典

        for vm, status in vm_statuses.items():
            display_name = f"{vm} ({status})"  # 显示状态
            if status == "running":
                QTreeWidgetItem(self.running_vm_tree, [display_name])
            else:
                QTreeWidgetItem(self.stopped_vm_tree, [display_name])

        self.update_button_states()  # 更新按钮状态

    def update_button_states(self):
        selected_item = self.running_vm_tree.currentItem() or self.stopped_vm_tree.currentItem()
        if selected_item:
            vm_name = selected_item.text(0).split(" (")[0]  # 获取虚拟机名称
            current_state = execute_ssh_command(f"virsh domstate {vm_name}").strip()
            self.start_vm_button.setEnabled(True)
            self.stop_vm_button.setEnabled(True)
            self.clone_vm_button.setEnabled(True)
            self.delete_vm_button.setEnabled(True)
        else:
            self.start_vm_button.setEnabled(False)
            self.stop_vm_button.setEnabled(False)
            self.clone_vm_button.setEnabled(False)
            self.delete_vm_button.setEnabled(False)

    def filter_vms(self):
        search_term = self.search_input.text().lower()
        for i in range(self.running_vm_tree.topLevelItemCount()):
            item = self.running_vm_tree.topLevelItem(i)
            item.setHidden(search_term not in item.text(0).lower())
        for i in range(self.stopped_vm_tree.topLevelItemCount()):
            item = self.stopped_vm_tree.topLevelItem(i)
            item.setHidden(search_term not in item.text(0).lower())
        self.update_button_states()  # 更新按钮状态

    def display_vm_info(self, item):
        vm_name = item.text(0).split(" (")[0]  # 获取虚拟机名称
        info = get_vm_info(vm_name)
        self.vm_info_text.setPlainText(info)
        self.update_button_states()  # 更新按钮状态

    def delete_vm(self, vm_name):
        # 创建旋转动画
        loading_gif = QMovie("delkvm.gif")  # 使用您的 GIF 文件
        loading_label = QLabel("正在删除虚拟机，请稍候...")
        loading_label.setMovie(loading_gif)
        loading_gif.start()

        # 创建一个新的对话框来显示 GIF
        gif_dialog = QDialog(self)
        gif_dialog.setWindowTitle("删除虚拟机")
        gif_dialog.setModal(True)
        
        # 设置对话框大小
        gif_dialog.resize(180, 180)  # 根据需要调整大小

        layout = QVBoxLayout()
        layout.addWidget(loading_label)
        gif_dialog.setLayout(layout)

        gif_dialog.show()

        # 创建并启动删除线程
        self.delete_thread = DeleteVMThread(vm_name)
        self.delete_thread.finished.connect(lambda result: self.on_delete_finished(result, gif_dialog))
        self.delete_thread.start()

    def on_delete_finished(self, result, gif_dialog):
        gif_dialog.close()  # 关闭 GIF 对话框
        QMessageBox.information(self, "删除虚拟机", result)  # 显示结果
        self.refresh_vm_list()  # 自动刷新虚拟机列表

    def delete_selected_vm(self):
        selected_item = self.running_vm_tree.currentItem() or self.stopped_vm_tree.currentItem()
        if selected_item:
            vm_name = selected_item.text(0).split(" (")[0]  # 获取虚拟机名称
            confirm = QMessageBox.question(self, "确认删除", f"确定要删除虚拟机 {vm_name} 吗？", QMessageBox.Yes | QMessageBox.No)
            if confirm == QMessageBox.Yes:
                self.delete_vm(vm_name)  # 调用类内的 delete_vm 方法
                self.refresh_vm_list()

    def start_selected_vm(self):
        selected_item = self.running_vm_tree.currentItem() or self.stopped_vm_tree.currentItem()
        if selected_item:
            vm_name = selected_item.text(0).split(" (")[0]  # 获取虚拟机名称
            self.execute_vm_operation(vm_name, "start")
        else:
            QMessageBox.warning(self, "错误", "请先选择一个虚拟机。")

    def stop_selected_vm(self):
        selected_item = self.running_vm_tree.currentItem() or self.stopped_vm_tree.currentItem()
        if selected_item:
            vm_name = selected_item.text(0).split(" (")[0]  # 获取虚拟机名称
            self.execute_vm_operation(vm_name, "stop")
        else:
            QMessageBox.warning(self, "错误", "请先选择一个虚拟机。")

    def open_settings_dialog(self):
        dialog = SettingsDialog()
        dialog.exec_()

    def clone_vm(self):
        selected_item = self.running_vm_tree.currentItem() or self.stopped_vm_tree.currentItem()
        if not selected_item:
            QMessageBox.warning(self, "警告", "请先选择一个虚拟机进行克隆。")
            return

        vm_name = selected_item.text(0).split(" (")[0]  # 获取当前选中的虚拟机名称

        while True:
            new_vm_name, ok = QInputDialog.getText(self, "输入新虚拟机名称", "请输入新虚拟机名称：")
            if not ok or not new_vm_name:
                return

            # 检查新虚拟机名称是否重复
            existing_vms = list_vms()
            if new_vm_name in existing_vms:
                QMessageBox.warning(self, "警告", "虚拟机名称已存在，请输入一个不同的名称。")
                continue  # 重新输入名称

            break  # 名称有效，退出循环

        command = f"virt-clone --original {vm_name} --name {new_vm_name} --auto-clone"

        # 创建旋转动画
        loading_gif = QMovie("jingxi.gif")  # 使用您的 GIF 文件
        loading_label = QLabel("请耐心等待，克隆中...")
        loading_label.setMovie(loading_gif)
        loading_gif.start()

        # 创建一个新的对话框来显示 GIF
        gif_dialog = QDialog(self)
        gif_dialog.setWindowTitle("克隆虚拟机")
        gif_dialog.setModal(True)
        
        # 设置对话框大小
        gif_dialog.resize(220, 220)  # 根据需要调整大小

        layout = QVBoxLayout()
        layout.addWidget(loading_label)
        gif_dialog.setLayout(layout)

        gif_dialog.show()

        # 创建并启动克隆线程
        self.clone_thread = CloneVMThread(command)
        self.clone_thread.finished.connect(lambda result: self.on_clone_finished(result, gif_dialog))
        self.clone_thread.start()

    def on_clone_finished(self, result, gif_dialog):
        gif_dialog.close()  # 关闭 GIF 对话框
        QMessageBox.information(self, "克隆虚拟机", result)  # 显示结果
        self.refresh_vm_list()  # 自动刷新虚拟机列表

    def create_vm(self):
        while True:
            vm_name, ok = QInputDialog.getText(self, "输入虚拟机名称", "请输入虚拟机名称：")
            if not ok or not vm_name:
                return

            existing_vms = list_vms()
            if vm_name in existing_vms:
                QMessageBox.warning(self, "警告", "虚拟机名称已存在，请输入一个不同的名称。")
                continue

            break

        cpu_num, ok = QInputDialog.getInt(self, "输入核心数", "请输入核心数：", 4, 1)
        if not ok:
            return

        mem_num, ok = QInputDialog.getInt(self, "输入内存大小", "请输入内存大小（G）：", 4, 1)
        if not ok:
            return

        disk_num, ok = QInputDialog.getInt(self, "输入硬盘大小", "请输入硬盘大小（G）：", 10, 1)
        if not ok:
            return

        iso_name = self.select_iso_file()
        if not iso_name:
            return

        # 建立 SSH 连接
        client = ssh_connect(settings["REMOTE_HOST"], settings["REMOTE_USER"], settings["REMOTE_PASS"])
        if not client:
            QMessageBox.warning(self, "错误", "无法连接到远程主机。")
            return

        # 生成磁盘文件
        disk_path = f"{settings['DISK_PATH'].rstrip('/')}/{vm_name}.qcow2"
        create_disk_command = f"qemu-img create -f qcow2 {disk_path} {disk_num}G"
        execute_ssh_command(create_disk_command)  # 创建磁盘文件

        # 使用默认 XML 模板
        xml_content = self.generate_vm_xml(vm_name, cpu_num, mem_num, disk_num, iso_name)

        # 写入 XML 文件到 KVM 默认存放路径
        xml_file_path = f"/etc/libvirt/qemu/{vm_name}.xml"  # KVM 默认存放路径
        write_xml_command = f"cat <<EOF > {xml_file_path}\n{xml_content}\nEOF"
        execute_ssh_command(write_xml_command)  # 写入 XML 文件

        # 创建旋转动画
        loading_gif = QMovie("addkvm.gif")  # 使用您的 GIF 文件
        loading_label = QLabel("正在创建虚拟机，请稍候...")
        loading_label.setMovie(loading_gif)
        loading_gif.start()

        # 创建一个新的对话框来显示 GIF
        gif_dialog = QDialog(self)
        gif_dialog.setWindowTitle("创建虚拟机")
        gif_dialog.setModal(True)
        gif_dialog.resize(180, 180)

        layout = QVBoxLayout()
        layout.addWidget(loading_label)
        gif_dialog.setLayout(layout)

        gif_dialog.show()

        # 定义并启动虚拟机
        define_result = execute_ssh_command(f"virsh define {xml_file_path}")
        if "error" in define_result.lower():
            QMessageBox.warning(self, "错误", f"定义虚拟机失败: {define_result}")
            gif_dialog.close()
            return

        start_result = execute_ssh_command(f"virsh start {vm_name}")
        if "error" in start_result.lower():
            QMessageBox.warning(self, "错误", f"启动虚拟机失败: {start_result}")
            gif_dialog.close()
            return

        gif_dialog.close()  # 关闭 GIF 对话框
        self.refresh_vm_list()  # 自动刷新虚拟机列表

    def select_iso_file(self):
        iso_files = list_iso_files()
        
        # 创建搜索框和列表
        dialog = QDialog(self)
        dialog.setWindowTitle("选择镜像文件")
        layout = QVBoxLayout()

        search_input = QLineEdit()
        search_input.setPlaceholderText("搜索镜像文件...")
        layout.addWidget(search_input)

        iso_list_widget = QListWidget()
        iso_list_widget.addItems(iso_files)
        layout.addWidget(iso_list_widget)

        # 过滤功能
        def filter_iso_files():
            search_term = search_input.text().lower()
            for i in range(iso_list_widget.count()):
                item = iso_list_widget.item(i)
                item.setHidden(search_term not in item.text().lower())

        search_input.textChanged.connect(filter_iso_files)

        select_button = QPushButton("选择")
        select_button.clicked.connect(lambda: dialog.accept())
        layout.addWidget(select_button)

        dialog.setLayout(layout)
        dialog.exec_()

        selected_items = iso_list_widget.selectedItems()
        return selected_items[0].text() if selected_items else None

    def generate_vm_xml(self, vm_name, cpu_num, mem_num, disk_num, iso_name):
        # 获取系统架构
        arch = execute_ssh_command("uname -m").strip()
        
        if arch == "x86_64":
            machine_type = "pc-q35-7.2"
            loader_path = "/usr/share/edk2/x86_64/QEMU_EFI-pflash.raw"
            emulator_path = "/usr/libexec/qemu-kvm"
            features = "<features><acpi/><pae/></features>"
        elif arch == "aarch64":
            machine_type = "virt"
            loader_path = "/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw"
            emulator_path = "/usr/bin/qemu-system-aarch64"
            features = "<features><acpi/><gic version='3'/></features>"
        else:
            raise ValueError("Unsupported architecture")

        return f"""<domain type='kvm'>
  <name>{vm_name}</name>
  <uuid>$(uuidgen)</uuid>
  <memory unit='KiB'>{mem_num * 1024}</memory>
  <currentMemory unit='KiB'>{mem_num * 1024}</currentMemory>
  <vcpu placement='static'>{cpu_num}</vcpu>

  <os>
    <type arch='{arch}' machine='virt-6.2'>hvm</type>
    <loader readonly='yes' type='pflash'>/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw</loader>
    <nvram>/var/lib/libvirt/qemu/nvram/{vm_name}_VARS.fd</nvram>
    <boot dev='hd'/>
    <boot dev='cdrom'/>
  </os>

  <features>
    <acpi/>
    <gic version='3'/>
  </features>

  <cpu mode='host-passthrough' check='none'/>
  <clock offset='utc'/>

  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>destroy</on_crash>

  <devices>
    <emulator>/usr/bin/qemu-system-aarch64</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2'/>
      <source file="{settings['DISK_PATH']}/{vm_name}.qcow2"/>
      <target dev='sda' bus='scsi'/>
      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
    </disk>
    <disk type='file' device='cdrom'>
      <driver name='qemu' type='raw'/>
      <source file='{iso_name}'/>
      <target dev='sdb' bus='scsi'/>
      <readonly/>
      <address type='drive' controller='0' bus='0' target='0' unit='1'/>
    </disk>
    <controller type='usb' index='0' model='qemu-xhci' ports='15'>
      <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
    </controller>
    <controller type='scsi' index='0' model='virtio-scsi'>
      <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
    </controller>
    <controller type='pci' index='0' model='pcie-root'/>
    <controller type='virtio-serial' index='0'>
      <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
    </controller>
    <controller type='pci' index='1' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='1' port='0x8'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0' multifunction='on'/>
    </controller>
    <controller type='pci' index='2' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='2' port='0x9'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
    </controller>
    <controller type='pci' index='3' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='3' port='0xa'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
    </controller>
    <controller type='pci' index='4' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='4' port='0xb'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x3'/>
    </controller>
    <controller type='pci' index='5' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='5' port='0xc'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x4'/>
    </controller>
    <controller type='pci' index='6' model='pcie-root-port'>
      <model name='pcie-root-port'/>
      <target chassis='6' port='0xd'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x5'/>
    </controller>
    <interface type='bridge'>
      <source bridge='br0'/>
      <model type='virtio'/>
    </interface>
    <serial type='pty'>
      <target type='system-serial' port='0'>
        <model name='pl011'/>
      </target>
    </serial>
    <console type='pty'>
      <target type='serial' port='0'/>
    </console>
    <channel type='unix'>
      <target type='virtio' name='org.qemu.guest_agent.0'/>
      <address type='virtio-serial' controller='0' bus='0' port='1'/>
    </channel>
    <input type='keyboard' bus='usb'>
      <address type='usb' bus='0' port='1'/>
    </input>
    <input type='mouse' bus='usb'>
      <address type='usb' bus='0' port='2'/>
    </input>
    <input type='tablet' bus='usb'>
      <address type='usb' bus='0' port='3'/>
    </input>
    <graphics type='vnc' port='-1' autoport='yes'>
      <listen type='address'/>
    </graphics>
    <video>
      <model type='virtio' heads='1' primary='yes'/>
      <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
    </video>
  </devices>
</domain>
"""

    def execute_vm_operation(self, vm_name, action):
        client = ssh_connect(settings["REMOTE_HOST"], settings["REMOTE_USER"], settings["REMOTE_PASS"])
        if not client:
            return

        try:
            # 检查虚拟机是否存在
            check_command = f"virsh dominfo {vm_name}"
            exit_status, output, error = execute_command(client, check_command)
            if exit_status != 0:
                QMessageBox.warning(self, "错误", f"虚拟机 {vm_name} 不存在。")
                return

            if action == "start":
                result = start_vm(client, vm_name)
            elif action == "stop":
                result = force_shutdown_vm(client, vm_name)
            else:
                return
            self.refresh_vm_list()  # 更新虚拟机列表
        finally:
            client.close()

    def open_vnc_interface(self):
        selected_item = self.running_vm_tree.currentItem()
        if not selected_item:
            QMessageBox.warning(self, "错误", "请先选择一个虚拟机。")
            return

        vm_name = selected_item.text(0).split(" (")[0]  # 获取虚拟机名称
        local_host = settings["LOCAL_HOST"]  # 获取本机IP地址
        remote_host = settings["REMOTE_HOST"]
        remote_user = settings["REMOTE_USER"]
        remote_pass = settings["REMOTE_PASS"]

        # 建立 SSH 连接
        client = ssh_connect(remote_host, remote_user, remote_pass)
        if not client:
            QMessageBox.warning(self, "错误", "无法连接到远程主机。")
            return

        # 执行 VNC 命令
        vnc_command = f"export DISPLAY={local_host}:0.0; vncviewer $(virsh vncdisplay {vm_name})"

        # 在已建立的 SSH 连接上执行命令
        exit_status, output, error = execute_command(client, vnc_command)
        if exit_status != 0:
            QMessageBox.warning(self, "错误", f"打开 VNC 界面失败: {error}")

        client.close()  # 关闭 SSH 连接

class DeleteVMThread(QThread):
    finished = pyqtSignal(str)  # 定义一个信号，用于传递结果

    def __init__(self, vm_name):
        super().__init__()
        self.vm_name = vm_name

    def run(self):
        # 获取虚拟机的磁盘路径
        disk_name = execute_ssh_command(f"virsh domblklist {self.vm_name} | grep qcow2 | awk '{{print $2}}'").strip()
        
        command_undefine = f"virsh undefine {self.vm_name} --nvram"
        
        result_undefine = execute_ssh_command(command_undefine)
        
        # 删除磁盘文件
        if disk_name:
            command_remove_disk = f"rm -rf {disk_name}"  # 直接使用 disk_name
            result_remove_disk = execute_ssh_command(command_remove_disk)
        else:
            result_remove_disk = "未找到磁盘路径。"

        if "error" in result_undefine.lower():
            result_undefine_retry = execute_ssh_command(f"virsh undefine {self.vm_name}")
            result_undefine += f"\n重试取消定义: {result_undefine_retry}"

        self.finished.emit(f"{result_undefine}\n{result_remove_disk}")  # 发射信号，传递结果

def ssh_connect(hostname, username, password):
    transport = paramiko.Transport((hostname, 22))  # 连接到主机的 SSH 端口
    try:
        transport.connect(username=username, password=password)  # 直接使用密码连接
        client = paramiko.SSHClient()
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        client._transport = transport  # 将 transport 赋值给 client
        return client
    except Exception as e:
        return None

def execute_command(client, command):
    stdin, stdout, stderr = client.exec_command(command)
    exit_status = stdout.channel.recv_exit_status()
    output = stdout.read().decode().strip()
    error = stderr.read().decode().strip()
    return exit_status, output, error

def start_vm(client, vm_name):
    command = f"virsh start {vm_name}"
    exit_status, output, error = execute_command(client, command)
    if exit_status == 0:
        return f"成功启动虚拟机: {vm_name}"
    else:
        return f"启动失败: {error}"

def force_shutdown_vm(client, vm_name):
    # 尝试使用 virsh destroy 关闭虚拟机
    command = f"virsh destroy {vm_name}"
    exit_status, output, error = execute_command(client, command)
    
    if exit_status == 0:
        return f"成功关闭虚拟机: {vm_name}"
    else:
        return f"关闭失败: {error}"

def load_settings():
    global settings
    try:
        with open('settings.json', 'r') as f:
            settings = json.load(f)
    except FileNotFoundError:
        settings = {
            "REMOTE_HOST": "",
            "REMOTE_USER": "",
            "REMOTE_PASS": "",
            "LOCAL_HOST": "",
            "LOG_FILE": "vm_manager.log",
            "DISK_PATH": "",
            "ISO_PATH": "",
            "XML_TEMPLATE": ""
        }

app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_() 